"""
Discovery command for CodeMCP CLI.

Automatically discovers entry points, main functions, and project structure
across multiple programming languages.
"""

import argparse
import asyncio
from typing import List, Dict, Any, Optional
from pathlib import Path
import json

from ...core.entry_discovery import EntryPointDiscovery, EntryDiscoveryConfig
from ...core.project_scanner import ProjectScanner, ProjectScanConfig
from ...core.function_classifier import FunctionClassifier, ClassificationConfig
from ...core.error_handler import CodeMCPError, log_info, log_debug
from ..base_command import CLICommand


class DiscoverCommand(CLICommand):
    """Command for discovering entry points and project structure."""
    
    def add_parser(self, subparsers) -> argparse.ArgumentParser:
        """Add discover command parser."""
        parser = subparsers.add_parser(
            'discover',
            help='Discover entry points and analyze project structure',
            description='Automatically discover main functions, entry points, and project structure',
            formatter_class=argparse.RawDescriptionHelpFormatter,
            epilog="""
Examples:
  codemcp discover                           # Discover in current directory
  codemcp discover --path /path/to/project   # Discover in specific directory
  codemcp discover --language python        # Only discover Python entry points
  codemcp discover --include-tests           # Include test files in discovery
  codemcp discover --min-confidence 0.8     # Set minimum confidence threshold
  codemcp discover --output-format json     # Output as JSON
  codemcp discover --save-results results.json  # Save results to file
            """.strip()
        )
        
        # Path options
        parser.add_argument(
            '--path', '-p',
            type=str,
            default='.',
            help='Project path to analyze (default: current directory)'
        )
        parser.add_argument(
            '--files',
            nargs='+',
            help='Specific files to analyze (optional)'
        )
        
        # Language filtering
        parser.add_argument(
            '--language', '-l',
            choices=['python', 'cpp', 'typescript', 'fortran', 'all'],
            default='all',
            help='Language to focus on (default: all)'
        )
        parser.add_argument(
            '--languages',
            nargs='+',
            choices=['python', 'cpp', 'typescript', 'fortran'],
            help='Multiple languages to include'
        )
        
        # Discovery options
        parser.add_argument(
            '--include-tests',
            action='store_true',
            help='Include test files in discovery'
        )
        parser.add_argument(
            '--include-examples',
            action='store_true',
            help='Include example files in discovery'
        )
        parser.add_argument(
            '--min-confidence',
            type=float,
            default=0.7,
            help='Minimum confidence threshold (0.0-1.0, default: 0.7)'
        )
        parser.add_argument(
            '--max-results',
            type=int,
            default=50,
            help='Maximum number of results to show (default: 50)'
        )
        
        # Output options
        parser.add_argument(
            '--show-details',
            action='store_true',
            help='Show detailed information about each entry point'
        )
        parser.add_argument(
            '--group-by',
            choices=['file', 'language', 'type'],
            default='file',
            help='Group results by file, language, or type (default: file)'
        )
        parser.add_argument(
            '--sort-by',
            choices=['confidence', 'name', 'file', 'line'],
            default='confidence',
            help='Sort results by confidence, name, file, or line (default: confidence)'
        )
        parser.add_argument(
            '--save-results',
            type=str,
            help='Save discovery results to file (JSON format)'
        )
        
        # Analysis depth
        parser.add_argument(
            '--deep-scan',
            action='store_true',
            help='Perform deep project scanning with build system analysis'
        )
        parser.add_argument(
            '--scan-dependencies',
            action='store_true',
            help='Include external dependencies in scan'
        )
        
        return parser
    
    async def execute(self, args: argparse.Namespace) -> int:
        """Execute the discover command."""
        try:
            self.printer.print_info("🔍 Starting project discovery...")
            
            # Validate path
            project_path = Path(args.path).resolve()
            if not project_path.exists():
                raise CodeMCPError(f"Project path does not exist: {project_path}")
            
            # Setup discovery configuration
            discovery_config = self._create_discovery_config(args)
            scan_config = self._create_scan_config(args)
            
            # Initialize discovery components
            entry_discovery = EntryPointDiscovery(discovery_config)
            project_scanner = ProjectScanner(scan_config)
            
            # Perform discovery
            results = await self._perform_discovery(
                entry_discovery, project_scanner, project_path, args
            )
            
            # Process and display results
            await self._display_results(results, args)
            
            # Save results if requested
            if args.save_results:
                await self._save_results(results, args.save_results)
            
            self.printer.print_success("✅ Discovery completed successfully!")
            return 0
            
        except Exception as e:
            self.printer.print_error(f"Discovery failed: {e}")
            log_debug(f"Discovery error details: {e}")
            return 1
    
    def _create_discovery_config(self, args: argparse.Namespace) -> EntryDiscoveryConfig:
        """Create entry point discovery configuration."""
        # Determine languages to include
        languages = []
        if args.language != 'all':
            languages = [args.language]
        elif args.languages:
            languages = args.languages
        else:
            languages = ['python', 'cpp', 'typescript', 'fortran']
        
        return EntryDiscoveryConfig(
            languages=languages,
            ignore_tests=not args.include_tests,
            ignore_examples=not args.include_examples,
            min_confidence=args.min_confidence,
            max_results_per_file=args.max_results
        )
    
    def _create_scan_config(self, args: argparse.Namespace) -> ProjectScanConfig:
        """Create project scan configuration."""
        return ProjectScanConfig(
            deep_scan=args.deep_scan,
            include_dependencies=args.scan_dependencies,
            ignore_build_dirs=True,
            ignore_cache_dirs=True,
            max_depth=10 if not args.deep_scan else 20
        )
    
    async def _perform_discovery(
        self, 
        entry_discovery: EntryPointDiscovery,
        project_scanner: ProjectScanner,
        project_path: Path,
        args: argparse.Namespace
    ) -> Dict[str, Any]:
        """Perform the actual discovery process."""
        results = {
            'project_path': str(project_path),
            'entry_points': [],
            'project_info': {},
            'statistics': {}
        }
        
        with self.progress.spinner("Scanning project structure..."):
            # Scan project structure
            project_info = project_scanner.scan_project(project_path)
            results['project_info'] = project_info
        
        with self.progress.spinner("Discovering entry points..."):
            # Discover entry points
            if args.files:
                # Analyze specific files
                file_paths = [str(Path(f).resolve()) for f in args.files]
                entry_points = entry_discovery.discover_entry_points(
                    project_path, file_paths
                )
            else:
                # Analyze entire project
                entry_points = entry_discovery.discover_entry_points(project_path)
            
            results['entry_points'] = [ep.__dict__ for ep in entry_points]
        
        # Generate statistics
        results['statistics'] = self._generate_statistics(
            entry_points, project_info
        )
        
        return results
    
    def _generate_statistics(
        self, 
        entry_points: List[Any], 
        project_info: Dict[str, Any]
    ) -> Dict[str, Any]:
        """Generate discovery statistics."""
        stats = {
            'total_entry_points': len(entry_points),
            'by_language': {},
            'by_type': {},
            'by_confidence': {
                'high': 0,  # >= 0.9
                'medium': 0,  # >= 0.7
                'low': 0    # < 0.7
            },
            'files_analyzed': project_info.get('total_files', 0),
            'languages_detected': len(project_info.get('languages', []))
        }
        
        for ep in entry_points:
            # Count by language
            lang = ep.language if hasattr(ep, 'language') else ep.get('language', 'unknown')
            stats['by_language'][lang] = stats['by_language'].get(lang, 0) + 1
            
            # Count by type
            ep_type = ep.entry_type if hasattr(ep, 'entry_type') else ep.get('entry_type', 'unknown')
            stats['by_type'][ep_type] = stats['by_type'].get(ep_type, 0) + 1
            
            # Count by confidence
            confidence = ep.confidence if hasattr(ep, 'confidence') else ep.get('confidence', 0)
            if confidence >= 0.9:
                stats['by_confidence']['high'] += 1
            elif confidence >= 0.7:
                stats['by_confidence']['medium'] += 1
            else:
                stats['by_confidence']['low'] += 1
        
        return stats
    
    async def _display_results(self, results: Dict[str, Any], args: argparse.Namespace):
        """Display discovery results."""
        entry_points = results['entry_points']
        project_info = results['project_info']
        statistics = results['statistics']
        
        # Display project information
        self.printer.print_header("📁 Project Information")
        self.formatter.print_key_value_pairs({
            "Project Path": results['project_path'],
            "Total Files": project_info.get('total_files', 'Unknown'),
            "Languages": ', '.join(project_info.get('languages', [])),
            "Build Systems": ', '.join(project_info.get('build_systems', [])) or 'None detected'
        })
        
        # Display statistics
        self.printer.print_header("📊 Discovery Statistics")
        self.formatter.print_key_value_pairs({
            "Total Entry Points": statistics['total_entry_points'],
            "High Confidence": statistics['by_confidence']['high'],
            "Medium Confidence": statistics['by_confidence']['medium'],
            "Low Confidence": statistics['by_confidence']['low']
        })
        
        if statistics['by_language']:
            self.printer.print_subheader("By Language:")
            for lang, count in statistics['by_language'].items():
                self.printer.print_info(f"  {lang}: {count}")
        
        if statistics['by_type']:
            self.printer.print_subheader("By Type:")
            for ep_type, count in statistics['by_type'].items():
                self.printer.print_info(f"  {ep_type}: {count}")
        
        # Display entry points
        if entry_points:
            self.printer.print_header("🎯 Discovered Entry Points")
            
            # Sort entry points
            sorted_points = self._sort_entry_points(entry_points, args.sort_by)
            
            # Group entry points if requested
            if args.group_by == 'file':
                self._display_grouped_by_file(sorted_points, args)
            elif args.group_by == 'language':
                self._display_grouped_by_language(sorted_points, args)
            elif args.group_by == 'type':
                self._display_grouped_by_type(sorted_points, args)
            else:
                self._display_entry_points_list(sorted_points, args)
        else:
            self.printer.print_warning("No entry points found.")
    
    def _sort_entry_points(self, entry_points: List[Dict], sort_by: str) -> List[Dict]:
        """Sort entry points by specified criteria."""
        if sort_by == 'confidence':
            return sorted(entry_points, key=lambda ep: ep.get('confidence', 0), reverse=True)
        elif sort_by == 'name':
            return sorted(entry_points, key=lambda ep: ep.get('name', ''))
        elif sort_by == 'file':
            return sorted(entry_points, key=lambda ep: ep.get('file_path', ''))
        elif sort_by == 'line':
            return sorted(entry_points, key=lambda ep: ep.get('line_number', 0))
        else:
            return entry_points
    
    def _display_grouped_by_file(self, entry_points: List[Dict], args: argparse.Namespace):
        """Display entry points grouped by file."""
        files_map = {}
        for ep in entry_points:
            file_path = ep.get('file_path', 'Unknown')
            if file_path not in files_map:
                files_map[file_path] = []
            files_map[file_path].append(ep)
        
        for file_path, eps in files_map.items():
            self.printer.print_subheader(f"📄 {file_path}")
            for ep in eps:
                self._display_entry_point(ep, args, indent=2)
    
    def _display_grouped_by_language(self, entry_points: List[Dict], args: argparse.Namespace):
        """Display entry points grouped by language."""
        lang_map = {}
        for ep in entry_points:
            language = ep.get('language', 'Unknown')
            if language not in lang_map:
                lang_map[language] = []
            lang_map[language].append(ep)
        
        for language, eps in lang_map.items():
            self.printer.print_subheader(f"🔤 {language.title()}")
            for ep in eps:
                self._display_entry_point(ep, args, indent=2)
    
    def _display_grouped_by_type(self, entry_points: List[Dict], args: argparse.Namespace):
        """Display entry points grouped by type."""
        type_map = {}
        for ep in entry_points:
            ep_type = ep.get('entry_type', 'Unknown')
            if ep_type not in type_map:
                type_map[ep_type] = []
            type_map[ep_type].append(ep)
        
        for ep_type, eps in type_map.items():
            self.printer.print_subheader(f"🏷️  {ep_type.title()}")
            for ep in eps:
                self._display_entry_point(ep, args, indent=2)
    
    def _display_entry_points_list(self, entry_points: List[Dict], args: argparse.Namespace):
        """Display entry points as a simple list."""
        for i, ep in enumerate(entry_points, 1):
            self.printer.print_info(f"{i}. ", end="")
            self._display_entry_point(ep, args)
    
    def _display_entry_point(self, ep: Dict, args: argparse.Namespace, indent: int = 0):
        """Display a single entry point."""
        indent_str = "  " * indent
        
        # Basic info
        name = ep.get('name', 'Unknown')
        ep_type = ep.get('entry_type', 'unknown')
        confidence = ep.get('confidence', 0)
        language = ep.get('language', 'unknown')
        
        # Confidence indicator
        if confidence >= 0.9:
            confidence_indicator = "🟢"
        elif confidence >= 0.7:
            confidence_indicator = "🟡"
        else:
            confidence_indicator = "🔴"
        
        # Format basic line
        basic_info = f"{indent_str}{confidence_indicator} {name} ({ep_type}) - {language}"
        self.printer.print_info(basic_info)
        
        if args.show_details:
            # Show detailed information
            details = {
                "File": ep.get('file_path', 'Unknown'),
                "Line": ep.get('line_number', 'Unknown'),
                "Confidence": f"{confidence:.2f}",
                "Signature": ep.get('signature', 'N/A')
            }
            
            for key, value in details.items():
                self.printer.print_info(f"{indent_str}  {key}: {value}")
            
            # Show context if available
            context = ep.get('context', {})
            if context:
                self.printer.print_info(f"{indent_str}  Context: {context}")
    
    async def _save_results(self, results: Dict[str, Any], output_file: str):
        """Save discovery results to file."""
        try:
            output_path = Path(output_file)
            with open(output_path, 'w', encoding='utf-8') as f:
                json.dump(results, f, indent=2, ensure_ascii=False)
            
            self.printer.print_success(f"💾 Results saved to {output_path}")
            
        except Exception as e:
            self.printer.print_error(f"Failed to save results: {e}")